package cn.csg.ams.collect2.poptopics.service.impl;

import cn.csg.ams.collect2.poptopics.dao.MkPrPfOutageRecoveryMonDao;
import cn.csg.ams.collect2.poptopics.entity.bo.MkPrPfOutageRecoveryMonBO;
import cn.csg.ams.collect2.poptopics.entity.vo.MkPrPfOutageRecoveryMonVO;
import cn.csg.ams.collect2.poptopics.infrastructure.constant.UserCategoryConstant;
import cn.csg.ams.collect2.poptopics.service.MkMcTermAlarmLogService;
import cn.csg.ams.collect2.poptopics.service.MkPrPfOutageRecoveryMonService;
import cn.csg.ams.collect2.poptopics.infrastructure.utils.DateUtil;
import cn.hutool.core.map.MapUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author: zhanggang
 * @ClassName: MkPrPfOutageRecoveryMonServiceImpl
 * @date: 2023-11-21 15:14:56
 * @Description: 实时停复电监测(mk_pr_pf_outage_recovery_mon)表服务实现类
 */
@Service("mkPrPfOutageRecoveryMonService")
public class MkPrPfOutageRecoveryMonServiceImpl implements MkPrPfOutageRecoveryMonService {

    @Autowired
    private MkPrPfOutageRecoveryMonDao mkPrPfOutageRecoveryMonDao;

    @Autowired
    private MkMcTermAlarmLogService alarmLogService;

    // @Autowired
    // private KafkaProducer<String, String> kafkaProducer;


    /**
     * 插入实时停复电监测数据
     *
     * @param leafList        所有叶子节点信息
     * @param outageStartTime 停电开始时间（yyyyMMddHHmmss）
     * @param prepositionTime 主站接受到数据的时间（yyyyMMddHHmmss）
     * @param eventId         事件id
     * @param startTimes      接受数据时间
     */
    @Override
    public void saveRecoveryByLeafList(List<MkPrPfOutageRecoveryMonVO> leafList, String outageStartTime, String prepositionTime, String eventId, Long startTimes, long systemTime) {
        if (CollectionUtils.isEmpty(leafList)) {
            return;
        }
        // ds
        String ds = outageStartTime.substring(0, 6);

        // 停电开始时间
        outageStartTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(DateUtil.parse(outageStartTime, "yyyyMMddHHmmss"));

        // 影响台区数
        long affectTaCount = leafList.stream().filter(vo -> UserCategoryConstant.PUBLIC_TA.equals(vo.getObjType()) || UserCategoryConstant.isSpecialtyTa(vo.getObjType())).map(MkPrPfOutageRecoveryMonVO::getTaNo)
                // .distinct()
                .count();

        // 影响分支数量-总数
        long affectBranchCount = leafList.stream().filter(vo -> UserCategoryConstant.BRANCH.equals(vo.getObjType())).map(MkPrPfOutageRecoveryMonVO::getLowBranchNo)
                // .distinct()
                .count();
        // 影响分支数量-按台区分组
        Map<String, Long> affectBranchTaCount = leafList.stream()
                .filter(vo -> UserCategoryConstant.BRANCH.equals(vo.getObjType()))
                .collect(Collectors.toMap(MkPrPfOutageRecoveryMonVO::getTaNo, vo -> 1L, Long::sum));

        // 影响低压用户数量-总数
        long affectUserCount = leafList.stream().filter(vo -> UserCategoryConstant.LOW_USER.equals(vo.getObjType())).map(MkPrPfOutageRecoveryMonVO::getObjId)
                // .distinct()
                .count();
        // 影响用户数量-按台区分组
        Map<String, Long> affectUserTaCount = leafList.stream().filter(vo -> UserCategoryConstant.LOW_USER.equals(vo.getObjType())).collect(Collectors.toMap(MkPrPfOutageRecoveryMonVO::getTaNo, vo -> 1L, Long::sum));
        // 影响用户数量-按台分支分组
        Map<String, Long> affectUserBranchCount = leafList.stream().filter(vo -> UserCategoryConstant.BRANCH.equals(vo.getObjType())).collect(Collectors.toMap(MkPrPfOutageRecoveryMonVO::getLowBranchNo, vo -> 1L, Long::sum));


        for (MkPrPfOutageRecoveryMonVO vo : leafList) {
            // 停电事件id (当为计划停电、故障停电、欠费停电中的一种时，事件id就用原本的事件id)
            if (eventId != null) {
                vo.setOutageEventId(eventId);
            }
            // 对象id, 对象类型，对象名称
            setObjIdAndName(vo, affectTaCount, affectBranchCount, affectBranchTaCount, affectUserCount, affectUserTaCount, affectUserBranchCount);
            // 其他为未知用户类型, 用运行电能表标识作为objId
            if (vo.getObjId() == null) {
                vo.setObjId(vo.getRunMeterId());
            }
            // 停电状态（1-停电，2-复电）
            vo.setOutageStatus("1");
            // 停电开始时间
            vo.setOutageStartTime(outageStartTime);
            // 停电类型：1：计划停电；2：故障停电；6：欠费停电; 4:三种停电类型都不是 （都匹配不上的设置为 2-故障停电）
            if (vo.getOutageType() == null) {
                vo.setOutageType("2");
            }
            // 研判类型 (1-告警研判，2-负荷研判)
            vo.setJudgeType("1");
            // 单双边告警类型 0无告警 1单边 2双边
            vo.setAlarmType("1");
            // 主站接受时间
            vo.setPrepositionTime(prepositionTime);
            // 开始时间
            vo.setStartTimes(startTimes);
            // 结束时间
            vo.setEndTimes(System.currentTimeMillis());
            // ds
            vo.setDs(ds);
            // 数据进入datahub的时间
            vo.setDatahubPushTime(systemTime);
        }

        // 看看是否已经存在的某些对象的停电数据
        // deleteRepeatAndUpdate(leafList);

        // // 将结果推送到datahub alarmPushStatus, failReason
        // String faultReason = sendDatahub.sendToOutageMon(leafList);
        // String alarmPushStatus = "1";
        // if (faultReason != null) {
        //     // 推送失败原因不为空则为推送失败
        //     alarmPushStatus = "2";
        // }
        // // 修改推送状态
        // String outagePushTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        // for (MkPrPfOutageRecoveryMonVO vo : leafList) {
        //     vo.setOutagePushTime(outagePushTime);
        //     vo.setAlarmPushStatus(alarmPushStatus);
        //     vo.setFailReason(faultReason);
        // }

        // 分批插入数据库，每次插入100条
        int start = 0;
        while (start < leafList.size()) {
            int end = Math.min(start + 100, leafList.size());
            mkPrPfOutageRecoveryMonDao.insertBatch(leafList.subList(start, end));
            start = end;
        }

        // 插入后，判断是否满足线路停电条件
        // checkLineOutage(leafList);

        // 推送停电事件到kafka
        // pushKafkaAndDatahub(leafList);

    }


    /**
     * 检查是否满足线路停电条件
     */
    private void checkLineOutage(List<MkPrPfOutageRecoveryMonVO> list) {
        MkPrPfOutageRecoveryMonVO vo = list.get(0);
        String taNo = vo.getTaNo();
        String lineNo = vo.getLineNo();

        // 查询该线路下停电台区的数据,分支数量，用户数量
        List<Map<String, Object>> objCountList = mkPrPfOutageRecoveryMonDao.getObjCountByLineNo(lineNo);
        Map<String, Long> objCount = objCountList.stream().collect(Collectors.toMap(
                        map -> MapUtil.getStr(map, "obj_type"),
                        map -> MapUtil.getLong(map, "count")
                )
        );
        // 台区停电数量
        long taOutageCount = MapUtil.getLong(objCount, "2", 0L) + MapUtil.getLong(objCount, "3", 0L);
        // 线路停电数量
        Long lineOutageCount = MapUtil.getLong(objCount, "1", 0L);
        if (taOutageCount < 3 || lineOutageCount > 0) {
            return;
        }

        // 若台区停电数量大于3，则视为线路停电，插入一条线路停电数据

        // 查询出线路下已经停电的对象，修改其事件id
        List<MkPrPfOutageRecoveryMonVO> voList = mkPrPfOutageRecoveryMonDao.getListByLineNoAndNotTaNo(lineNo, taNo);
        voList.forEach(o -> o.setOutageEventId(vo.getOutageEventId()));

        // 删除原来的数据
        LambdaQueryWrapper<MkPrPfOutageRecoveryMonBO> wrapper = Wrappers.<MkPrPfOutageRecoveryMonBO>lambdaQuery()
                .eq(MkPrPfOutageRecoveryMonBO::getLineNo, lineNo)
                .eq(MkPrPfOutageRecoveryMonBO::getOutageStatus, "1")
                .ne(MkPrPfOutageRecoveryMonBO::getTaNo, taNo);
        mkPrPfOutageRecoveryMonDao.delete(wrapper);

        // 创建线路的停电数据
        MkPrPfOutageRecoveryMonVO lineVo = new MkPrPfOutageRecoveryMonVO();
        BeanUtils.copyProperties(vo, lineVo);
        lineVo.setObjId(lineNo);
        lineVo.setObjName(vo.getLineName());
        lineVo.setObjType("1");
        lineVo.setAffectedSubareaCt(taOutageCount);
        lineVo.setAffectedLowvBrCt(MapUtil.getLong(objCount, "4", 0L));
        lineVo.setAffectedCustomerCt(MapUtil.getLong(objCount, "5", 0L));
        voList.add(lineVo);

        // 分批插入数据库，每次插入100条
        int start = 0;
        while (start < voList.size()) {
            int end = Math.min(start + 100, voList.size());
            mkPrPfOutageRecoveryMonDao.insertBatch(voList);
            start = end;
        }

        // 添加到list中，用于后面推送
        list.add(lineVo);
    }


    /**
     * 插入前，若已经存在某些对象的停电数据，就更新这些数据的停电事件id
     * 由于无法更新逐渐，先将重复数据查出来，在删除，在将停电事件id设置为新值，最后一起插入
     *
     * @param leafList
     */
    public void deleteRepeatAndUpdate(List<MkPrPfOutageRecoveryMonVO> leafList) {
        // 查询是否有重复数据
        List<String> objIds = leafList.stream().map(MkPrPfOutageRecoveryMonVO::getObjId).collect(Collectors.toList());
        LambdaQueryWrapper<MkPrPfOutageRecoveryMonBO> wrapper = Wrappers.<MkPrPfOutageRecoveryMonBO>lambdaQuery()
                .select(MkPrPfOutageRecoveryMonBO::getObjId, MkPrPfOutageRecoveryMonBO::getOutageStartTime)
                .in(MkPrPfOutageRecoveryMonBO::getObjId, objIds)
                .eq(MkPrPfOutageRecoveryMonBO::getOutageStatus, "1");
        List<MkPrPfOutageRecoveryMonBO> list = mkPrPfOutageRecoveryMonDao.selectList(wrapper);
        if (CollectionUtils.isEmpty(list)) {
            // 没有重复数据
            return;
        }

        // 删除重复数据
        mkPrPfOutageRecoveryMonDao.delete(wrapper);

        // 重复数据的停电开始时间
        Map<String, String> map = list.stream().collect(Collectors.toMap(
                MkPrPfOutageRecoveryMonBO::getObjId,
                MkPrPfOutageRecoveryMonBO::getOutageStartTime,
                (value1, value2) -> value2
        ));

        // 已有的重复数据停电开始时间用原来的
        for (MkPrPfOutageRecoveryMonVO vo : leafList) {
            String outageStartTime = map.get(vo.getObjId());
            if (outageStartTime != null) {
                vo.setOutageStartTime(outageStartTime);
            }
        }
    }


    /**
     * 根据对象类型（用户类型）设置objType、objId、objName
     *
     * @param vo                    实时停复电对象
     * @param affectTaCount         影响台区数量
     * @param affectBranchCount     影响分支数量
     * @param affectBranchTaCount   影响分支数量-按台区分组
     * @param affectUserCount       影响低压用户数量
     * @param affectUserTaCount     影响用户数量-按台区分组
     * @param affectUserBranchCount 影响用户数量-按台分支分组
     */
    private void setObjIdAndName(MkPrPfOutageRecoveryMonVO vo, long affectTaCount, long affectBranchCount, Map<String, Long> affectBranchTaCount, long affectUserCount, Map<String, Long> affectUserTaCount, Map<String, Long> affectUserBranchCount) {
        // 对象类型（用户类型）
        String objType = vo.getObjType();

        // 线路
        if (UserCategoryConstant.LINE.equals(objType)) {
            vo.setObjType("1");
            vo.setObjId(vo.getLineNo());
            vo.setObjName(vo.getLineName());
            // 影响台区数量
            vo.setAffectedSubareaCt(affectTaCount);
            // 影响分支数量
            vo.setAffectedLowvBrCt(affectBranchCount);
            // 影响低压用户数量
            vo.setAffectedCustomerCt(affectUserCount);
            return;
        }

        // 公变台区
        if (UserCategoryConstant.PUBLIC_TA.equals(objType)) {
            vo.setObjType("2");
            vo.setObjId(vo.getTaNo());
            vo.setObjName(vo.getTaName());
            // 影响分支数量
            vo.setAffectedLowvBrCt(affectBranchTaCount.getOrDefault(vo.getTaNo(), 0L));
            // 影响低压用户数量
            vo.setAffectedCustomerCt(affectUserTaCount.getOrDefault(vo.getTaNo(), 0L));
            return;
        }

        // 专变台区
        if (UserCategoryConstant.isSpecialtyTa(objType)) {
            vo.setObjType("3");
            vo.setObjId(vo.getTaNo());
            vo.setObjName(vo.getTaName());
            // 专变台区影响用户数默认为1
            vo.setAffectedCustomerCt(1L);
            return;
        }

        // 分支
        if (UserCategoryConstant.BRANCH.equals(objType)) {
            vo.setObjType("4");
            vo.setObjId(vo.getLowBranchNo());
            vo.setObjName(vo.getLowBranchName());
            // 影响低压用户数量
            vo.setAffectedCustomerCt(affectUserBranchCount.getOrDefault(vo.getLowBranchNo(), 0L));
            return;
        }

        // 低压用户
        if (UserCategoryConstant.LOW_USER.equals(objType)) {
            vo.setObjType("5");
        }
    }


    /**
     * 匹配计划停电与故障停电
     *
     * @param taNo            台区编号
     * @param outageStartTime 停电开始时间
     */
    public void matchPlanAndFualtEvent(String taNo, String outageStartTime, List<MkPrPfOutageRecoveryMonVO> list) {
        // 计划开始时间 - 30分钟 <= 停电开始时间 <= 计划结束时间
        Date time = DateUtil.parse(outageStartTime, "yyyyMMddHHmmss");
        String leftTime = DateUtil.format(DateUtils.addMinutes(time, -30), "yyyy-MM-dd HH:mm:ss");
        String rightTime = DateUtil.format(DateUtils.addMinutes(time, 30), "yyyy-MM-dd HH:mm:ss");

        // 匹配计划停电
        Map<String, String> planEvent = mkPrPfOutageRecoveryMonDao.getPlanOutageEvent(taNo, leftTime, rightTime);
        if (planEvent != null) {
            // 匹配到计划停电，设置停电类型，匹配工单编号，停电范围，停电描述
            for (MkPrPfOutageRecoveryMonVO vo : list) {
                // 设置 (停电类型：1：计划停电；2：故障停电；6：欠费停电) 字段
                vo.setOutageType("1");
                vo.setOrderNo(MapUtil.getStr(planEvent, "pf_event_id"));
                vo.setOutageDescription(MapUtil.getStr(planEvent, "pf_cause"));
                vo.setOutageScope(MapUtil.getStr(planEvent, "pf_range"));
            }
            return;
        }

        // 匹配故障停电
        Map<String, String> faultEvent = mkPrPfOutageRecoveryMonDao.getfaultOutageEvent(taNo, leftTime, rightTime);
        if (faultEvent != null) {
            // 匹配到故障停电，设置停电类型，匹配工单编号，停电范围，停电描述
            for (MkPrPfOutageRecoveryMonVO vo : list) {
                // 设置 (停电类型：1：计划停电；2：故障停电；6：欠费停电) 字段
                vo.setOutageType("2");
                vo.setOrderNo(MapUtil.getStr(faultEvent, "pf_event_id"));
                vo.setOutageDescription(MapUtil.getStr(faultEvent, "fault_desc"));
                vo.setOutageScope(MapUtil.getStr(faultEvent, "pf_range"));
            }
        }

    }


    /**
     * 输出线路停电
     *
     * @param lineNo          线路编号
     * @param outageStartTime 停电开始时间
     */
    @Override
    public void lineOutage(String lineNo, String outageStartTime) {
        // mkPrPfOutageRecoveryMonDao.lineOutage();
    }
}
